package auth

import (
	"context"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"io/ioutil"
	"os"
	"serve/shared/antu/token"
	"serve/shared/id"
	"strings"
)

const (
	authorizationHeader = "authorization"
	bearerPrefix        = "Bearer "
)

type tokenVerifier interface {
	Verify(token string) (string, error)
}

type interceptor struct {
	verifier tokenVerifier
}

//Interceptor 装载publicKey
func Interceptor(publicKey string) (grpc.UnaryServerInterceptor, error) {
	f, err := os.Open(publicKey)

	if err != nil {
		return nil, fmt.Errorf(" os.Open(publicKey) err :%v", err)
	}

	all, err := ioutil.ReadAll(f)

	if err != nil {
		return nil, fmt.Errorf("ioutil.ReadAll(f) err :%v", err)
	}

	pem, err := jwt.ParseRSAPublicKeyFromPEM(all)
	if err != nil {
		return nil, fmt.Errorf("jwt.ParseRSAPublicKeyFromPEM(all) err :%v", err)

	}
	i := &interceptor{
		verifier: &token.JWTTokenVerifier{PublicKey: pem}, // 验证jwt具体实现
	}
	return i.HandleReq, nil
}

//HandleReq 负责验证jwt并且写入context的处理函数
func (i *interceptor) HandleReq(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	tkn, err := tokenFormContext(ctx)
	if err != nil {
		return nil, status.Error(codes.Unauthenticated, "")
	}
	aid, err := i.verifier.Verify(tkn)
	if err != nil {
		return nil, status.Error(codes.Unauthenticated, "token 参数错误")
	}
	return handler(ContextWithAccountId(ctx, id.AccountID(aid)), req)
}

//tokenFormContext 从context 里获取 token
func tokenFormContext(ctx context.Context) (string, error) {
	incomingContext, ok := metadata.FromIncomingContext(ctx) // 从context获取所以heads，因为这里是http协议，所以获取 的是heads
	unauthenticated := status.Errorf(codes.Unauthenticated, "")
	if !ok {
		return "", unauthenticated
	}

	tkn := ""
	for _, v := range incomingContext[authorizationHeader] {
		if strings.HasPrefix(v, bearerPrefix) {
			tkn = v[len(bearerPrefix):]
		}
	}

	if tkn == "" {
		return "", unauthenticated
	}

	return tkn, nil
}

type accountIdKey struct {
}

//ContextWithAccountId context写入accointid
func ContextWithAccountId(ctx context.Context, aid id.AccountID) context.Context {
	return context.WithValue(ctx, accountIdKey{}, aid) // 将aid写入context
}

//AccountIDFromContext 返回aid
func AccountIDFromContext(c context.Context) (id.AccountID, error) {
	v := c.Value(accountIdKey{}) // 从根据key从context中获取val，获取到的是一个接口类型
	aid, ok := v.(id.AccountID)  // 断言他是字符串
	if !ok {                     // 如果不成功就返回错误
		return "", status.Error(codes.Unauthenticated, "")
	}
	return aid, nil
}
